#ifndef CONNMGR_HPP
#define CONNMGR_HPP

#include "PacketProcessor.hpp"

#include <memory>

#include <boost/cstdint.hpp>
#include <boost/unordered_map.hpp>
#include <boost/ptr_container/ptr_map.hpp>
#include <boost/ptr_container/ptr_unordered_map.hpp>
#include <boost/thread.hpp>

namespace kieper {
    class Device;
    class Message;
    class Packet;
    class IncomingPacket;

    typedef boost::ptr_unordered_map<uint16_t, Device> DeviceMap;
    typedef std::auto_ptr<DeviceMap> DeviceMapPtr;
    typedef boost::unordered_map<uint32_t, IncomingPacket*> Packets;
    typedef boost::unordered_map<uint32_t, boost::condition_variable*> Conditions;
    typedef boost::unordered_map<uint32_t, boost::mutex*> Mutexes;

    class ConnMgr {
    public:
        virtual ~ConnMgr();
        void initialize();
        void startPacketProcessor();
        DeviceMapPtr findDevices(); 
        void send(const Device& device, const Message& message);
        void putResponsePacket(uint32_t packetId, IncomingPacket* packet);
        void putCondition(uint32_t packetId, boost::condition_variable* condition);
        void putMutex(uint32_t packetId, boost::mutex* mtx);
        IncomingPacket* getNextPacket();
        bool responseArrived(uint32_t packetId);
        bool isThreadWaitingForResponse(uint32_t packetId);
        std::auto_ptr<IncomingPacket> getResponse(uint32_t packetId);
        boost::condition_variable* getCondition(uint32_t packetId);
        boost::mutex* getMutex(uint32_t packetId);
        bool isPacketProcessorReady();
    protected:
        ConnMgr();
    private:
        bool isInitialized_;
        uint32_t nextMsgId_;
        Packets packets_;
        Conditions conditions_;
        Mutexes mutexes_;
        PacketProcessor packetProcessor_;

        uint32_t getNextMsgId();
        virtual void doInitialize() = 0;
        virtual DeviceMapPtr doFindDevices() = 0;
        virtual void doSend(const Device& device, const Packet& packet) = 0;
        virtual IncomingPacket* doGetNextPacket() = 0;
    };
}

#endif
